package org.hidroinformatica.shapes.feature;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Iterator;
import java.util.Map.Entry;

import org.hidroinformatica.shapes.exceptions.FeatureException;
import org.hidroinformatica.shapes.util.FeatureUtil;
import org.opengis.feature.simple.SimpleFeature;

/**
 * Classe responsável por realizar a leitura de uma feature (SimpleFeature). Não instancie
 * diretamente esta classe, utilize a classe ShapeFileReader que contém o procedimento 
 * automatizado.
 * 
 * @author Carlos A. Junior (CIH - Centro Internacional de Hidroinformática)
 */
public class FeatureReader implements Serializable
{
	private static final long serialVersionUID = -6664387995634473604L;
	
	private Definition definition;
	
	private FeatureUtil util;
	
	public FeatureReader(Definition definition)
	{
		super();
		
		this.definition = definition;
		this.util		= new FeatureUtil();
	}
	
	/**
	 * Processa a leitura de uma feature do shapefile.
	 * 
	 * @param SimpleFeature feature
	 * 
	 * @param targetClass
	 * 
	 * @param precisionModel
	 * 
	 * @return Serializable
	 * 
	 * @throws FeatureException
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 */
	@SuppressWarnings("rawtypes")
	private Serializable readFeature (SimpleFeature feature, Class targetClass, int precisionModel) throws FeatureException, InstantiationException, IllegalAccessException
	{
		Iterator<Entry<String, String>> definicoes = this.getDefinition().getIteratorDefinitions();
		
		Object object = targetClass.newInstance();
		
		while (definicoes.hasNext())
		{
			Entry<String, String> entry = (Entry<String, String>) definicoes.next();
			
			if(feature.getAttribute(entry.getKey()) == null)
			{
				continue;
			}
			
			Object shapeColumnValue = feature.getAttribute(entry.getKey());
			
			try
			{
				Object attributeValue;
				Field atributo;
				
				if(this.getUtil().hasRule(entry.getValue()))
				{
					atributo = this.getUtil().getRuledField(object, entry.getValue());
				}
				else
				{
					atributo = this.getUtil().getField(object, entry.getValue());
				}
				
				// Libera acesso ao atributo.
				atributo.setAccessible(true);
				
				if(this.getUtil().hasRule(entry.getValue()))
				{
					attributeValue = atributo.get(object);
					
					if(attributeValue == null)
					{
						attributeValue = atributo.getType().newInstance();
					}
					
					Field recursiveAttribute = this.getUtil().getRuledAttributeField(attributeValue, entry.getValue());
					
					recursiveAttribute.setAccessible(true);
					recursiveAttribute.set(attributeValue, this.getUtil().getParsedValue(shapeColumnValue, recursiveAttribute));
				}
				else
				{
					attributeValue = this.getUtil().getParsedValue(shapeColumnValue, atributo);
				}
				
				// Seta os dados no objeto principal.
				atributo.set(object, attributeValue);
				
			} catch (SecurityException e) {
				e.printStackTrace();
			} catch (NoSuchFieldException e) {
				e.printStackTrace();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		if(object != null)
		{
			this.getUtil().setGeometrySRID(object, precisionModel);
		}
		return (Serializable) object;
	}
	
	/**
	 * Extrai os dados de uma feature para dentro de uma classe de destino.
	 * 
	 * @param feature
	 * 
	 * @param targetClass
	 * 
	 * @return Serializable
	 * 
	 * @throws FeatureException
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 */
	@SuppressWarnings("rawtypes")
	public Serializable getObject (SimpleFeature feature, Class targetClass) throws FeatureException, InstantiationException, IllegalAccessException
	{
		return this.readFeature(feature, targetClass, -1);
	}
	
	/**
	 * Extrai os dados de uma feature para dentro de uma classe de destino.
	 * 
	 * @param feature
	 * 
	 * @param targetClass
	 * 
	 * @param precisionModel
	 * 
	 * @return Serializable
	 * 
	 * @throws FeatureException
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 */
	@SuppressWarnings("rawtypes")
	public Serializable getObject (SimpleFeature feature, Class targetClass, int precisionModel) throws FeatureException, InstantiationException, IllegalAccessException
	{
		return this.readFeature(feature, targetClass, precisionModel);
	}

	public Definition getDefinition()
	{
		return definition;
	}

	public FeatureUtil getUtil() {
		return util;
	}

	public void setUtil(FeatureUtil util) {
		this.util = util;
	}

	public void setDefinition(Definition definition) {
		this.definition = definition;
	}
}
